﻿using System;
using T0yK4T.Tools;
using ToyChat.Server;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ToyChat.Server.UI
{
    /// <summary>
    /// The class that contains the main entry point of the application
    /// </summary>
    class Program
    {
        /// <summary>
        /// The main entry point of the application
        /// </summary>
        /// <param name="args">The command-line arguments passed from the user</param>
        static void Main(string[] args)
        {
            new ExecutionWrapper().RunServer();
        }
    }

    /// <summary>
    /// Used to run and provide a simple user interface to the server
    /// </summary>
    public class ExecutionWrapper
    {
        /// <summary>
        /// The server that this <see cref="ExecutionWrapper"/> is currently using
        /// </summary>
        private IServer server;

        /// <summary>
        /// Indicates if the program should exit
        /// </summary>
        private bool exit = false;

        /// <summary>
        /// Exit Command
        /// </summary>
        public const string CMD_EXIT = "exit";

        /// <summary>
        /// Set Command
        /// </summary>
        public const string CMD_SET = "set";

        /// <summary>
        /// Owner Command
        /// </summary>
        public const string SET_OWNER = "owner";

        /// <summary>
        /// Create Command (Not yet implemented)
        /// </summary>
        public const string CMD_CREATE = "create";

        /// <summary>
        /// Create User Command (Not yet implemented)
        /// </summary>
        public const string CREATE_USER = "user";

        /// <summary>
        /// Create Channel Command (Not yet implemented)
        /// </summary>
        public const string CREATE_CHANNEL = "channel";

        /// <summary>
        /// Grant Command
        /// </summary>
        public const string CMD_GRANT = "grant";

        /// <summary>
        /// Deny Command
        /// </summary>
        public const string CMD_DENY = "deny";

        /// <summary>
        /// Runs the server and blocks the application from exiting until the exit command has been entered
        /// </summary>
        public void RunServer()
        {
            this.server = new Server(new ServerSettings()); // Loads defaults
            this.server.Start();
            this.BlockUntilExit();

            IUser[] users = this.server.UserHandler.ToArray();
            foreach (IUser user in users)
                user.Disconnect();
            this.server.Stop();
        }

        /// <summary>
        /// Continuously parses user input
        /// </summary>
        private void BlockUntilExit()
        {
            while (!exit)
                try { this.ParseCommand(Console.ReadLine()); }
                catch { }
            this.server.Dispose();
        }

        /// <summary>
        /// Attempts to parse the specified string as a command
        /// </summary>
        /// <param name="line">The string to attempt to parse</param>
        private void ParseCommand(string line)
        {
            string[] split = line.Split(' ');
            if (split.Length >= 1)
            {
                string[] sub = null;
                if (split.Length > 1)
                {
                    sub = new string[split.Length - 1];
                    Array.Copy(split, 1, sub, 0, sub.Length);
                }
                switch (split[0].ToLower())
                {
                    case CMD_EXIT:
                        exit = true;
                        break;
                    case CMD_SET:
                        this.Set(sub);
                        break;
                    case CMD_GRANT:
                        this.Grant(sub);
                        break;
                    case CMD_DENY:
                        this.Deny(sub);
                        break;
                }
            }
        }

        /// <summary>
        /// <see cref="ExecutionWrapper.CMD_DENY"/> executor
        /// </summary>
        /// <param name="args">Additional arguments for the command</param>
        private void Deny(params string[] args)
        {
            if (args == null || args.Length != 2)
            {
                Console.WriteLine("Deny takes 2 arguments [Permission] [Username]");
                string[] names = Enum.GetNames(typeof(PermissionFlags));
                Console.WriteLine("Valid Permissions Are:");
                foreach (string name in names)
                    Console.WriteLine(name);
                return;
            }
            PermissionFlags flags = PermissionFlags.None;
            if (!Enum.TryParse<PermissionFlags>(args[0], true, out flags))
            {
                Console.WriteLine("Cannot find permission {0}", args[0]);
                return;
            }
            else
            {
                IUserInfo info = this.server.GlobalDBHandler.FindUser(args[1]);
                if (info != null)
                {
                    info.Permissions = (info.Permissions | flags) ^ flags;
                    Console.WriteLine("Removed Permission {0} from user {1}", flags, info.Username);
                }
                else
                {
                    Console.WriteLine("Cannot find user {0}", args[1]);
                    return;
                }
                IUser user;
                if (info != null && this.server.UserHandler.TryGetUser(info.Username, out user))
                {
                    user.Info = info;
                    Console.WriteLine("User {0} is currently logged in, swapped Info");
                }
            }
        }

        /// <summary>
        /// <see cref="ExecutionWrapper.CMD_GRANT"/> executor
        /// </summary>
        /// <param name="args">Additional arguments for the command</param>
        private void Grant(params string[] args)
        {
            if (args == null || args.Length != 2)
            {
                Console.WriteLine("Grant takes 2 arguments [Permission] [Username]");
                string[] names = Enum.GetNames(typeof(PermissionFlags));
                Console.WriteLine("Valid Permissions Are:");
                foreach (string name in names)
                    Console.WriteLine(name);
                return;
            }
            PermissionFlags flags = PermissionFlags.None;
            if (!Enum.TryParse<PermissionFlags>(args[0], true, out flags))
            {
                Console.WriteLine("Cannot find permission {0}", args[0]);
                return;
            }
            else
            {
                IUserInfo info = this.server.GlobalDBHandler.FindUser(args[1]);
                if (info != null)
                {
                    info.Permissions |= flags;
                    Console.WriteLine("Added Permission {0} to user {1}", flags, info.Username);
                }
                else
                {
                    Console.WriteLine("Cannot find user {0}", args[1]);
                    return;
                }
                IUser user;
                if (info != null && this.server.UserHandler.TryGetUser(info.Username, out user))
                {
                    user.Info = info;
                    Console.WriteLine("User {0} is currently logged in, swapped Info");
                }
            }
        }

        /// <summary>
        /// <see cref="ExecutionWrapper.CMD_SET"/> executor
        /// </summary>
        /// <param name="args">Additional arguments for the command</param>
        private void Set(params string[] args)
        {
            if (args == null)
            {
                Console.WriteLine("Set takes two arguments [Type] [Username]");
                Console.WriteLine("Valid Types Are:");
                Console.WriteLine(SET_OWNER);
                return;
            }
            if (args.Length > 1)
            {
                switch (args[0])
                {
                    case SET_OWNER: // Value is username
                        IUserInfo info = this.server.GlobalDBHandler.FindUser(args[1]);
                        if (info != null)
                        {
                            info.Permissions = PermissionFlags.All;
                            this.server.GlobalDBHandler.StoreUser(info);
                            IUser loggedOn;
                            Console.WriteLine("User {0} is now Owner, stored in DB", info.Username);
                            if (this.server.UserHandler.TryGetUser(args[1], out loggedOn))
                            {
                                loggedOn.Info = info;
                                Console.WriteLine("User {0} was logged on, switched UserInfo", loggedOn.Username);
                            }
                        }
                        else
                            Console.WriteLine("User {0} was not found in DB", args[1]);

                        break;
                }
            }
        }
    }
}
